/**
 *
 */
package com.uaroadshow.webpay.processor.impl;

import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.demo2do.core.entity.result.HttpResult;
import com.demo2do.core.http.annotation.Rao;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.utils.StringUtils;
import com.demo2do.core.utils.XmlUtils;
import com.demo2do.core.wechat.entity.pay.PayConfig;
import com.demo2do.core.wechat.entity.pay.PrepayRequest;
import com.demo2do.core.wechat.entity.response.PrepayResponse;
import com.demo2do.core.wechat.rao.wechat.WechatPayRao;
import com.demo2do.core.wechat.utils.WechatUtils;
import com.uaroadshow.webpay.entity.PaymentContext;
import com.uaroadshow.webpay.entity.TradeType;
import com.uaroadshow.webpay.entity.config.WechatpayConfig;
import com.uaroadshow.webpay.entity.context.WechatpayContext;
import com.uaroadshow.webpay.entity.prepare.PrepareResponse;
import com.uaroadshow.webpay.helper.SignatureHelper;
import com.uaroadshow.webpay.processor.PaymentProcessor;

/**
 * @author downpour
 *
 */
@Component("wechatpayProcessor")
public class WechatpayProcessor implements PaymentProcessor {

    private static final Logger logger = LoggerFactory.getLogger(WechatpayProcessor.class);

    private WechatpayConfig wechatpayConfig;
    
    @Autowired
    @Qualifier("wechatpaySignatureHelper")
    private SignatureHelper signatureHelper;

    @Rao
    private WechatPayRao wechatPayRao;

    /**
     *
     * @param appId
     * @param partnerId
     * @param notifyUrl
     * @param spbillCreateIp
     */
    @Autowired
    public void initWechatpayConfig(@Value("#{payment['wechat.appId']}") String appId,
                                    @Value("#{payment['wechat.partnerId']}") String partnerId,
                                    @Value("#{payment['wechat.notifyUrl']}") String notifyUrl,
                                    @Value("#{payment['wechat.spbillCreateIp']}") String spbillCreateIp){
        
        // init static configurations
        this.wechatpayConfig = new WechatpayConfig(appId, partnerId, notifyUrl, spbillCreateIp);
        
    }

    /* (non-Javadoc)
     * @see com.continental.payment.handler.PaymentProcessor#prepare(com.continental.payment.entity.PaymentContext)
     */
    public PrepareResponse prepare(PaymentContext paymentContext) {
        
        PrepareResponse prepareResponse = new PrepareResponse();

        try {
            
            logger.trace("#prepare - Trying to retrieving prepayId from tencent.");
            
            String prepayId = this.retrievePrepayId(paymentContext);
            
            if (prepayId != null) {

                // initialize prepayId and go next step
                WechatpayContext wechatpayContext = new WechatpayContext(this.wechatpayConfig, prepayId);

                // use prepayId generate sign again
                if (wechatpayContext.isValid()) {

                    String signature = signatureHelper.sign(wechatpayContext.toSignatureContext());
                    wechatpayContext.sign(signature);

                    // put WeChatpayContext into payment response
                    prepareResponse.success().data(wechatpayContext);
                    
                } else {
                    
                    logger.error("#prepare - An error occurs when valid wechatpayContext[appId[{}], package[{}]].", wechatpayContext.getAppId(), wechatpayContext.getPackageValue());
                    
                    prepareResponse.fail().message("创建微信支付环境失败!");
                
                }
                
            } else {
                
                logger.error("#prepare - An error occurs when retreiving prepayId from tencent.");
                
                prepareResponse.fail().message("创建微信支付环境失败!");
            }
            
        } catch(Exception e) {
            
            logger.error("#prepare - An error occurs when creating wechatpay context.", e);
            
            prepareResponse.fail().message("创建微信支付环境失败!");
        
        }
        
        return prepareResponse;
    
    }

    /**
     *
     * @param paymentContext
     * @return
     */
    protected String retrievePrepayId(PaymentContext paymentContext) {
        
        // 1. construct prepay_request
        PrepayRequest prepayRequest = new PrepayRequest(this.wechatpayConfig, TradeType.JSAPI.value(), paymentContext);
        
        // 2. make sign and add to the prepay_request
        prepayRequest.sign(this.sign(prepayRequest.describe()));
        
        // 3. call RAO to get prepay_id
        HttpResult httpResult = wechatPayRao.getPreparePayId(prepayRequest);
        
        logger.info("#retrievePrepayId - call rao get result [{}]", JsonUtils.toJson(httpResult));
        
        if(httpResult.isValid()) {
            
            PrepayResponse prepayResponse = XmlUtils.toObject(httpResult.getResponseText(), PrepayResponse.class);
            
            logger.info("#retrievePrepayId - call rao get prepayResponse [{}]", JsonUtils.toJson(prepayResponse));

            if(WechatUtils.isValid(prepayResponse)) {
                
                String sign = this.sign(prepayResponse.describe());
                
                if(org.apache.commons.lang3.StringUtils.equals(sign, prepayResponse.getSign())) {
                    return prepayResponse.getPrepayId();
                }
                
            }
            
        }
        
        logger.error("#retrievePrepayId - call rao to get prepareId failed. ");
        
        return null;
        
    }

    /**
     * 
     * @param params
     * @return
     */
    private String sign(Map<String, String> params) {
        String queryString = StringUtils.toSortedQueryString(params, "sign") + "&key=" + signatureHelper.getAppKey();
        return DigestUtils.md5Hex(queryString).toUpperCase();
    }

    /* (non-Javadoc)
     * @see com.uaroadshow.webpay.processor.PaymentProcessor#validate(com.demo2do.core.wechat.entity.pay.PayConfig)
     */
    public boolean validate(PayConfig payConfig) {
        return payConfig.match(wechatpayConfig.getAppid(), wechatpayConfig.getSellerId());
    }

}
